﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace zijian666.AnyExtensions.Privates;

internal class PropertyReader
{
    private static readonly ConcurrentDictionary<Type, PropertyReader[]> CACHED = new();
    private static readonly PropertyReader[] EMPTY_ARRAY = [];
    public static PropertyReader[] GetInstance(Type type) => CACHED.GetOrAdd(type, Create);

    private static PropertyReader[] Create(Type type)
    {
        if (type is null || type.Assembly == typeof(int).Assembly)
        {
            return EMPTY_ARRAY;
        }

        var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty)
                        .Where(x => x.GetIndexParameters().Length == 0 && x.CanRead && x.IsDefined(typeof(ValidationAttribute)))
                        .ToList();

        if (properties.Count == 0)
        {
            return EMPTY_ARRAY;
        }


        return properties.Select(x => new PropertyReader(x)).ToArray();

    }

    private readonly Func<object, object> _getValue;

    public PropertyReader(PropertyInfo property)
    {
        Property = property ?? throw new ArgumentNullException(nameof(property));
        Validators = property.GetCustomAttributes<ValidationAttribute>().ToArray();
        var p = Expression.Parameter(typeof(object));
        var cast = Expression.Convert(p, property.DeclaringType);
        var get = Expression.Property(cast, Property);
        var ret = Expression.Convert(get, typeof(object));
        _getValue = Expression.Lambda<Func<object, object>>(ret, p).Compile();
    }

    public PropertyInfo Property { get; }

    public ValidationAttribute[] Validators { get; }

    public object GetValue(object instance) => _getValue(instance);
}